/**
 *   Test 1.6e
 */

#include ".\app_cfg.h"
#include ".\service\check_string\check_string.h"
#include ".\service\print_string\print_string.h"
#include ".\service\print_string\print_string_pool.h"
#include ".\service\byte_adapter\byte_adapter.h"
#include ".\service\task_check_use_peek\task_check_use_peek.h"

#define __PLOOC_CLASS_IMPLEMENT
#include "utilities/ooc.h"

//#include "cmsis_os2.h"
// =============================== print ===============================

def_simple_fsm(task_world,
               def_params(
                   fsm(print_string) tPrintString;
                   event_t *ptPrintEvent;
               ))
end_def_simple_fsm(task_world)

declare_fsm_implementation(task_world);
declare_fsm_initialiser(task_world, args(event_t *ptEvent))

def_simple_fsm(task_apple,
               def_params(
                   fsm(print_string) tPrintString;
                   event_t *ptPrintEvent;
               ))
end_def_simple_fsm(task_apple)

declare_fsm_implementation(task_apple);
declare_fsm_initialiser(task_apple, args(event_t *ptEvent))

def_simple_fsm(task_orange,
               def_params(
                   fsm(print_string) tPrintString;
                   event_t *ptPrintEvent;
               ))
end_def_simple_fsm(task_orange)

declare_fsm_implementation(task_orange);
declare_fsm_initialiser(task_orange, args(event_t *ptEvent))

// =============================== check ===============================

def_simple_fsm(check_apple,
               def_params(
                   fsm(check_string) tCheckString;
                   user_target_t *ptTarget;
               ))
end_def_simple_fsm(check_apple)
declare_fsm_implementation(check_apple, args(bool *pbIsRquestDrop))
declare_fsm_initialiser(check_apple, args(void *pTarget))

def_simple_fsm(check_orange,
               def_params(
                   fsm(check_string) tCheckString;
                   user_target_t *ptTarget;
               ))
end_def_simple_fsm(check_orange)
declare_fsm_implementation(check_orange, args(bool *pbIsRquestDrop))
declare_fsm_initialiser(check_orange, args(void *pTarget))

def_simple_fsm(check_world,
               def_params(
                   fsm(check_string) tCheckString;
                   user_target_t *ptTarget;
               ))
end_def_simple_fsm(check_world)
declare_fsm_implementation(check_world, args(bool *pbIsRquestDrop))
declare_fsm_initialiser(check_world, args(void *pTarget))

// =============================== in/out ===============================

def_simple_fsm(serial_out_task, def_params(uint8_t chByte;))
end_def_simple_fsm(serial_out_task)

declare_fsm_implementation(serial_out_task);
declare_fsm_initialiser(serial_out_task)

def_simple_fsm(serial_in_task)
end_def_simple_fsm(serial_in_task)

declare_fsm_implementation(serial_in_task);
declare_fsm_initialiser(serial_in_task)

static fsm(task_check_use_peek) s_tCheck;

static fsm(check_apple) s_tCheckApple;
static fsm(check_world) s_tCheckWorld;
static fsm(check_orange) s_tCheckOrange;

static fsm(task_world) s_tPrintWorld;
static fsm(task_apple) s_tPrintApple;
static fsm(task_orange) s_tPrintOrange;

static fsm(serial_in_task) s_tSerialTaskIn;
static fsm(serial_out_task) s_tSerialTaskOut;

#define COUNTOF(__ARRAY)    ((sizeof(__ARRAY))/(sizeof(__ARRAY[0])))

static byte_queue_t s_tFifoIn;
static byte_queue_t s_tFifoOut;

static uint8_t s_chFifoInBuffer[16];
static uint8_t s_chFifoOutBuffer[16];

static event_t s_tPrintAppleEvent;
static event_t s_tPrintWorldEvent;
static event_t s_tPrintOrangeEvent;

static bool get_byte(uint8_t *pchAddr);

static user_target_t s_tCheckAppleTarget  = {.fnGetByte = get_byte, .ptEvent = &s_tPrintAppleEvent };
static user_target_t s_tCheckWorldTarget  = {.fnGetByte = get_byte, .ptEvent = &s_tPrintWorldEvent };
static user_target_t s_tCheckOrangeTarget = {.fnGetByte = get_byte, .ptEvent = &s_tPrintOrangeEvent };

static fsm_rt_t check_apple_function(bool *pbRequestDrop);
static fsm_rt_t check_world_function(bool *pbRequestDrop);
static fsm_rt_t check_orange_function(bool *pbRequestDrop);

static check_agent_t s_tItemTable[3] = {
    {
        .fnCheck = check_apple_function,
        .ptTarget = &s_tCheckAppleTarget,
    }, {
        .fnCheck = check_world_function,
        .ptTarget = &s_tCheckWorldTarget,
    }, {
        .fnCheck = check_orange_function,
        .ptTarget = &s_tCheckOrangeTarget,
    }
};

void system_init(void);

/*********************************************************************
 *
 *       main()
 *
 *  Function description
 *   Application entry point.
 */

int main(void)
{
    system_init();

    INIT_BYTE_QUEUE(&s_tFifoIn, s_chFifoInBuffer, COUNTOF(s_chFifoInBuffer));
    INIT_BYTE_QUEUE(&s_tFifoOut, s_chFifoOutBuffer, COUNTOF(s_chFifoOutBuffer));

    INIT_EVENT(&s_tPrintAppleEvent, false, RESET_AUTO);
    INIT_EVENT(&s_tPrintWorldEvent, false, RESET_AUTO);
    INIT_EVENT(&s_tPrintOrangeEvent, false, RESET_AUTO);

    // register fsm
    init_fsm(check_apple, &s_tCheckApple, args(&s_tCheckAppleTarget));
    init_fsm(check_world, &s_tCheckWorld, args(&s_tCheckWorldTarget));
    init_fsm(check_orange, &s_tCheckOrange, args(&s_tCheckOrangeTarget));

    init_fsm(task_check_use_peek, &s_tCheck, args(&s_tFifoIn, s_tItemTable, COUNTOF(s_tItemTable)));
    init_fsm(task_apple, &s_tPrintApple, args(&s_tPrintAppleEvent));
    init_fsm(task_world, &s_tPrintWorld, args(&s_tPrintWorldEvent));
    init_fsm(task_orange, &s_tPrintOrange, args(&s_tPrintOrangeEvent));

    init_fsm(serial_in_task, &s_tSerialTaskIn);
    init_fsm(serial_out_task, &s_tSerialTaskOut);

    while (1) {
        breath_led();
        call_simple_fsm(task_check_use_peek, &s_tCheck);

        call_simple_fsm(task_world, &s_tPrintWorld);
        call_simple_fsm(task_apple, &s_tPrintApple);
        call_simple_fsm(task_orange, &s_tPrintOrange);

        call_simple_fsm(serial_in_task, &s_tSerialTaskIn);
        call_simple_fsm(serial_out_task, &s_tSerialTaskOut);
    }

}

static fsm_rt_t check_apple_function(bool *pbRequestDrop)
{
    return call_fsm(check_apple, &s_tCheckApple, args(pbRequestDrop));
}

static fsm_rt_t check_world_function(bool *pbRequestDrop)
{
    return call_fsm(check_world, &s_tCheckWorld, args(pbRequestDrop));
}

static fsm_rt_t check_orange_function(bool *pbRequestDrop)
{
    return call_fsm(check_orange, &s_tCheckOrange, args(pbRequestDrop));
}

fsm_initialiser(check_apple, args(void *pTarget))
init_body(
    this.ptTarget = pTarget;
    init_fsm(check_string, &this.tCheckString, args((uint8_t *)"apple", this.ptTarget->fnGetByte));
)

fsm_implementation(check_apple, args(bool *pbIsRquestDrop))
{
    def_states(CALL_STRING)

    body_begin()

        state(CALL_STRING) {
            if (fsm_rt_cpl == call_fsm(check_string, &this.tCheckString, args(pbIsRquestDrop))) {
                SET_EVENT(this.ptTarget->ptEvent);
                fsm_cpl();
            }
        }

    body_end()
}

fsm_initialiser(check_orange, args(void *pTarget))
init_body(
    this.ptTarget = pTarget;
    init_fsm(check_string, &this.tCheckString, args((uint8_t *)"orange", this.ptTarget->fnGetByte));
)

fsm_implementation(check_orange, args( bool *pbIsRquestDrop))
{
    def_states(CALL_STRING)
    body_begin()

        state(CALL_STRING) {
            if (fsm_rt_cpl == call_fsm(check_string, &this.tCheckString, args(pbIsRquestDrop))) {
                SET_EVENT(this.ptTarget->ptEvent);
                fsm_cpl();
            }
        }

    body_end()
}

fsm_initialiser(check_world, args(void *pTarget))
init_body(
    this.ptTarget = pTarget;
    init_fsm(check_string, &this.tCheckString, args((uint8_t *)"world", this.ptTarget->fnGetByte));
)
fsm_implementation(check_world, args(bool *pbIsRquestDrop))
{
    def_states(CALL_STRING, POST_EVENT)
    body_begin()

        state(CALL_STRING) {
            if (fsm_rt_cpl == call_fsm(check_string, &this.tCheckString, args(pbIsRquestDrop))) {
                SET_EVENT(this.ptTarget->ptEvent);
                fsm_cpl();
            }
        }

    body_end()
}
/* ======================= serial in task ======================= */
fsm_initialiser(serial_out_task)
init_body()

fsm_implementation(serial_out_task)
{
    def_states(OUT_CHAR, PRINT_CHAR)

    body_begin()

        state(OUT_CHAR) {
            if (DEQUEUE_BYTE(&s_tFifoOut, &this.chByte)) {
                update_state_to(PRINT_CHAR)
            } else {
                fsm_cpl();
            }
        }

        state(PRINT_CHAR) {
            if (serial_out(this.chByte)) {
                transfer_to(OUT_CHAR);
            }
        }

    body_end()
}

fsm_initialiser(serial_in_task)
init_body()

fsm_implementation(serial_in_task)
{
    def_states(RECV_CHAR)

    uint8_t chByte;

    body_begin()

        state(RECV_CHAR) {
            if (serial_in(&chByte)) {
                LOG_RAW("Recv %c\r\n", chByte);
                if (!ENQUEUE_BYTE(&s_tFifoIn, &chByte)) {
                    LOG_RAW("Enqueue failed\r\n");
                }
            }
        }

    body_end()
}

/* ======================= print world ======================= */
fsm_initialiser(task_world, args(event_t *ptEvent))
init_body(
    this.ptPrintEvent = ptEvent;
)

fsm_implementation(task_world)
{
    def_states(
        WAIT_PRINT_EVENT,
        PRINT_WORLD
    )

    body_begin()

        on_start(
            init_fsm(print_string, &this.tPrintString, args((uint8_t *)"world"));
        );

        state(WAIT_PRINT_EVENT) {
            if (WAIT_EVENT(this.ptPrintEvent)) {
                LOG_RAW("Recv print world event\r\n");
                transfer_to(PRINT_WORLD);
            }
        }

        state(PRINT_WORLD) {
            if (fsm_rt_cpl == call_simple_fsm(print_string, &this.tPrintString)) {
                fsm_cpl();
            }
        }

    body_end()
}

fsm_initialiser(task_apple, args(event_t *ptEvent))
init_body(
    this.ptPrintEvent = ptEvent;
)

fsm_implementation(task_apple)
{
    def_states(
        WAIT_PRINT_EVENT,
        PRINT_APPLE
    )

    body_begin();

        on_start(
            init_fsm(print_string, &this.tPrintString, args((uint8_t *)"apple"));
        );

        state(WAIT_PRINT_EVENT) {
            if (WAIT_EVENT(this.ptPrintEvent)) {
                LOG_RAW("Recv print apple event\r\n");
                transfer_to(PRINT_APPLE);
            }
        }

        state(PRINT_APPLE) {
            if (fsm_rt_cpl == call_simple_fsm(print_string, &this.tPrintString)) {
                fsm_cpl();
            }
        }

    body_end()
}


fsm_initialiser(task_orange, args(event_t *ptEvent))
init_body(
    this.ptPrintEvent = ptEvent;
)

fsm_implementation(task_orange)
{
    def_states(
        WAIT_PRINT_EVENT,
        PRINT_ORANGE
    )

    body_begin()

        on_start(
            init_fsm(print_string, &this.tPrintString, args((uint8_t *)"orange"));
        )

        state(WAIT_PRINT_EVENT) {
            if (WAIT_EVENT(this.ptPrintEvent)) {
                LOG_RAW("Recv print orange event\r\n");
                transfer_to(PRINT_ORANGE);
            }
        }

        state(PRINT_ORANGE) {
            if (fsm_rt_cpl == call_simple_fsm(print_string, &this.tPrintString)) {
                fsm_cpl();
            }
        }

    body_end()
}

static bool out_char(uint8_t chByte)
{
    return ENQUEUE_BYTE(&s_tFifoOut, &chByte);
}

static bool get_byte(uint8_t *pchAddr)
{
    return PEEK_BYTE_QUEUE(&s_tFifoIn, pchAddr);
}

/**
 * system_init
 */
void system_init()
{
    SystemInit();
    rcc_init();
    led_init();
    usart_init();
}

/*************************** End of file ****************************/
